/**
 * @author Martin Bories
 * @license GNU GPL v3
 * Please tell me about your projects: martin.christian.bories [at] googlemail [dot] com
 * Follow me on Google Buzz for changes, updates and more: https://profiles.google.com/115761324153391452188/buzz
 */
(function($) {

	/**
	 * Contains a database.
	 */
	JDB = $.Class.create({
		tables: null,
		rows: {

		},
		lists: {

		},
		options: {

		},

		/**
		 * Initializes the database.
	 	 * Makes sure, that the storage is generated.
		 */
		init: function(options) {
			$.extend(this.options, options);
			this.initStorage();
		},

		/**
		 * Makes sure the storage is generated.
		 */
		initStorage: function() {
			if ($.jStorage.get("jdb_master_tables") == null) $.jStorage.set("jdb_master_tables", {});

			return this;
		},

		/**
		 * Returns the table list, so, the content of the storage field "jdb_master_tables".
		 */
		getTables: function() {
			if (this.tables == null) this.tables = $.jStorage.get("jdb_master_tables");

			return this.tables;
		},

		/**
		 * Returns the structure of the table.
		 *
		 * @param name	Contains the name of the table.
		 */
		getTable: function(name) {
			var tables = this.getTables();
			return tables[name];
		},
	
		/**
	 	 * Creates a table, if that table doesn't exist.
	 	 *
	 	 * @param name	Contains the name of the new table.
	 	 * @param fields	Contains an array with the name of the fields. The field "id unique" is automatically added to the beginning of the table.
	 	 * @param options	Contains an object with the options. Default values and nearer description see below.
	 	 *
	 	 * Options:
	 	 *	id	boolean	Contains wether the "id" autofill field is added to the table or not. Default: true.
	 	 *	timestamp	boolean	Contains wether the "timestamp" autofill field is added to the table or not. Default: false
	 	 *	splitRows	boolean If true, the rows of the table are saved in several files, where the table gets an own storage file. If false, they are saved with the table data in the storage "jdb_master_tables". Default: false
	 	 *	overwrite	boolean	Overwrites an existing table. Default: false
	 	 */
		createTable: function(name, fields, options) {
			if (options == undefined) options = {};
			var tables = this.getTables();
			var table = {
				name: name,
				fields: fields,
				options: {
					id: true,
					timestamp: false,
					splitRows: false,
					overwrite: false
				},
				rows: new Array()
			};
			$.extend(table.options, options);
			options = table.options;

			//doesnt create table if the table already exists
			if (!options.overwrite && this.tableExists(name)) return;

			//add fields
			if (options.timestamp) fields.unshift("timestamp");
			if (options.id) fields.unshift("id");

			//insert table
			tables[name] = table;

			if (!options.splitRows) tables[name].rows = new Array();
		
			$.jStorage.set("jdb_master_tables", tables);

			//add row storage
			if (options.splitRows) {
				$.jStorage.set("jdb_tablerows_"+name, new Array());
			}

			return this;
		},

		/**
		 * Checks wether the table exists or not.
		 *
		 * @param name	Contains the name of the table.
	 	 */
		tableExists: function(name) {
			var table = this.getTable(name);
			return !(table == undefined);
		},

		/**
	 	 * Updates the table.
	 	 * Rows are not updated. Use "updateRows()" for this.
	 	 *
	 	 * @param table	Contains the table structure.
	 	 */
		updateTable: function(table) {
			var oldTable = this.getTable(table.name);
			var tables = this.getTables();
			table.rows = oldTable.rows;
			tables[table.name] = table;
			$.jStorage.set("jdb_master_tables", tables);

			return this;
		},

		/**
	 	 * Updates the rows.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param rows	Contains the rows of the table.
	 	 */
		updateRows: function(name, rows) {
			var table = this.getTable(name);
			if (!table.options.splitRows) {
				var tables = this.getTables();
				table.rows = rows;
				tables[name] = table;
				$.jStorage.set("jdb_master_tables", tables);
			}else $.jStorage.set("jdb_tablerows_"+name, rows);

			this.rows[name] = rows;

			return this;
		},
	
		/**
		 * Returns the rows of the table.
		 *
	 	 * @param name	Contains the name of the table.
	 	 */
		getRows: function(name) {
			if (this.rows[name] == undefined) {
				var table = this.getTable(name);
				if (!table.options.splitRows) this.rows[name] = table.rows;
				else this.rows[name] = $.jStorage.get("jdb_tablerows_"+name);
			}

			return this.rows[name];
		},

		/**
	 	 * Returns the row, specified by the id.
	 	 *
	 	 * @param name	Contains the name from the table.
	 	 * @param id	Contains the id from the row (zero-based).
	 	 */
		get: function(name, id) {
			return this.getRows(name)[id];
		},

		/**
	 	 * Returns the fields that must be filled by the user at that table. So, fields like "id" or "timestamp" will not be send with.
	 	 *
	 	 * @param fields	Contains the field list. It is very usefull to give it, because it may contain the labels, that makes it easier for the user to read the fields. However, the key is the name of the table field (as its used in the table) and the value the label for the field, used by the user.
	 	 */
		getUserfillFields: function(fields) {
			var fieldTMP = $.extend(true, {}, fields);
			delete fieldTMP.id;
			delete fieldTMP.timestamp;

			return fieldTMP;
		},

		/**
	 	 * Returns the next id (unique) for the table.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 */
		getNextID: function(name) {
			var id = 0;
			$.each(this.getRows(name), function(i, v) {++id;});
			return id;
		},

		/**
	 	 * Returns the list informations for the table if the list exists, otherwise "undefined".
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 */
		getList: function(name) {
			return this.lists[name];
		},

		/**
	 	 * Returns wether the table has a list or not.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 */
		hasList: function(name) {
			return !(this.getList(name) == undefined);
		},

		/**
	 	 * Inserts a row into a table.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param data	Contains some data for the row as an object.
		 *
		 * @return row	Contains the just created row including the autofill values.
	 	 */
		insert: function(name, data) {
			var table = this.getTable(name);
			var rows = this.getRows(name);
			var row = {};
			var id = this.getNextID(name);

			for (var i = 0; i < table.fields.length; ++i) {
				var field = table.fields[i];

				switch(field) {
					case 'id':
						row.id = id;
						break;
					case 'timestamp':
						row.timestamp = new Date().getTime();
						break;
					default:
						if (data[field] == undefined) data[field] = '';
						row[field] = data[field];
						break;
				}
			}

			//add to table
			rows[id] = row;
			this.updateRows(table.name, rows);

			//add to list
			if (this.hasList(name)) this.addRowToList(name, row, this.getList(name).options);

			//return row
			return row;
		},

		/**
	 	 * Updates a row.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param id	Contains the id of the row.
	 	 * @param data	Contains the new row data.
		 * @return row	Contains the just created row including the autofill data.
	 	 */
		update: function(name, id, data) {
			var rows = this.getRows(name);
			var row = this.get(name, id);
			$.extend(row, data);

			rows[id] = row;
			this.updateRows(name, rows);

			//return row
			return row;
		},

		/**
	 	 * Deletes a row.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param id	Contains the id of the table.
	 	 */
		remove: function(name, id) {
			var rows = this.getRows(name);
			rows[id] = null;
			this.updateRows(name, rows);

			return this;
		},

		/**
	 	 * Lists the database entries in a table inside of the element.
	 	 *
	 	 * @param name	Contains the name of the table that's content should be shown.
	 	 * @param element	Contains a jquery object to the element.
	 	 * @param fields	Contains an object with the fields. The key is the field name, that is used in the tables, too, the value is the field label, that will be displayed in the table.
	 	 * @param options	Contains an object with options. Description see below.
	 	 *
	 	 * Options:
	 	 *	addButton	boolean	Contains wether an "add button" should be drawn or not. Via the add button, the user may add new entries into the database- and the table as well. Default: true.
	 	 *	addButtonLabel	String	Contains the label that's drawn at the add button. Default: Language.get('add')
	 	 *	addButtonAddLabel	String	Contains the label at the "add" button in the add dialog. Default: Language.get('add')
	 	 *	addButtonCancelLabel	String	Contains the label at the "cancel" button in the add dialog. Default: Language.get('cancel')
	 	 *	addInDuration	int	Contains the duration for the fadeIn()-process after adding a new row. See jQuery.fadeIn() for more information. Default: 1500
	 	 *	editButton	boolean	Contains wether an "edit button" should be drawn or not. Via the edit button, the user may edit the row. Default: true
	 	 *	editButtonLabel	String	Contains the label for the edit button. Default: Language.get('edit')
	 	 *	editButtonEditLabel	String	Contains the label that's written on the edit button of the edit dialog. Default: Language.get('edit')
	 	 *	editButtonCancelLabel	String	Contains the label that's written on the cancel button of the edit dialog. Default: Language.get('cancel')
	 	 *	editValueInDuration	int	Contains the duration time for the new value after editing a row to appear. See "jQuery.fadeIn()" for more information. Default: 1500
	 	 *	editValueOutDuration	int	Contains the duration time for the old value after editing a row to disappear. See "jQuery.fadeOut()" for more information. Default: 1500
	 	 *	removeButton	boolean	Contains wether a "remove button" should be drawn or not. This button will be drawn at the left column to each row. By clicking it, the row will be removed and faded out in the table. Default: true
	 	 *	removeButtonLabel	String	Contains the title of the remove button. Default: Language.get('removeRow')
	 	 *	removeButtonConfirm	boolean	Contains wether a confirm should be shown on clicking the remove button or not. Default: true
	 	 *	removeButtonConfirmText	String	Contains the text that's shown on the confirm message. Default: Language.get('removeRowConfirm')
	 	 *	removeButtonRemoveLabel	String	Contains the text at the "remove" button in the confirm dialog. Default: Language.get('removeRow')
	 	 *	removeButtonCancelLabel	String	Contains the text at the "cancel" button in the confirm dialog. Default: Language.get('cancel')
	 	 *	removeOutDuration	int	Contains the duration time for the deleted row to disappear. See "jQuery.fadeOut()" for more information. Default: 1500
	 	 */
		list: function(name, element, fields, options) {
			if (!this.tableExists(name)) return;

			var table = this.getTable(name);
			//do options
			var o = {
				addButton: true,
				addButtonLabel: $.jlng('add'),
				addButtonAddLabel: $.jlng('add'),
				addButtonCancelLabel: $.jlng('cancel'),
				addInDuration: 1500,
				editButton: true,
				editButtonLabel: $.jlng('edit'),
				editButtonEditLabel: $.jlng('edit'),
				editButtonCancelLabel: $.jlng('cancel'),
				editValueOutDuration: 1500,
				editValueInDuration: 1500,
				removeButton: true,
				removeButtonLabel: $.jlng('removeRow'),
				removeButtonConfirm: true,
				removeButtonConfirmText: $.jlng('removeRowConfirm'),
				removeButtonRemoveLabel: $.jlng('removeRow'),
				removeButtonCancelLabel: $.jlng('cancel'),
				removeOutDuration: 1500
			};
			$.extend(o, options);

			if (fields == undefined) {
				fields = {};
				$.each(table.fields, function(i, v) {
					fields[v] = v;
				});
			}

			this.lists[name] = {
				name: name,
				element: element,
				fields: fields,
				options: o
			};

			//draw table layout
			element.append("<table class='jdb-table ui-widget ui-widget-content'><thead><tr class='ui-widget-header'></tr></thead><tbody></tbody></table>");

			//fetch rows of the table
			var rows = this.getRows(name);

			//draw thead
			$.each(fields, function(name, label) {
				$('thead tr', element).append('<th>'+label+'</th>');
			});

			//draw tbody
			$.each(rows, function(id, row) {
				if (row == null) return true;

				$('tbody', element).append('<tr>');
				$.each(fields, function(name, label) {
					$('tbody tr:last', element).append('<td class="'+name+'">'+row[name]+'</td>');
				});
				$('tbody', element).append('</tr>');
				$('tbody tr:last', element).data("row", row);
			});

			//draw add and remove buttons (if required)
			if (o.addButton) this.drawAddButton(name, element, fields, o);
			if (o.removeButton) this.drawRemoveButtons(name, element, o);
			if (o.editButton) this.drawEditButtons(name, element, fields, o);

			return this;
		},

		/**
	 	 * Adds a row to an already drawn list.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param row	Contains the row data.
	 	 * @param options	Contains an object with several options (description see at function "list()").
	 	 */
		addRowToList: function(name, row, options) {
			var list = this.getList(name);

			$('tbody', list.element).append("<tr style='display: none;'>");
			$.each(list.fields, function(name, label) {
				$('tbody tr:last', list.element).append('<td class="'+name+'">'+row[name]+'</td>');
			});
			$('tbody', list.element).append("</tr>");

			$('tbody tr:last', list.element).fadeIn(options.addInDuration);
			$('tbody tr:last', list.element).data("row", row);

			//add remove button to this element
			this.drawRemoveButton(name, $('tbody tr:last', list.element), list.options);
			this.drawEditButton(name, $('tbody tr:last', list.element), list.fields, list.options);

			return this;
		},

		/**
	 	 * Draws the add button for the table list.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param element	Contains the element in that the add button should be drawn.
	 	 * @param fields	Contains an object with the fields that should be listed, where the key is the real field name and the value is the label for the user. However, fields like "id" or "timestamp" which are autofilled by the system will be spliced.
	 	 * @param options	Contains an object with several options (description see at function "list()").
	 	 */
		drawAddButton: function(name, element, fields, options) {
			var self = this;

			//splice autofill fields
			fields = this.getUserfillFields(fields);

			//draw add button, add event
			element.prepend('<a href="#" title="'+options.addButtonLabel+'">'+options.addButtonLabel+'</a>');
			$('a:last', element).click(function() {
				self.openAddDialog(name);
			});

			//draw add dialog
			element.append('<div style="display: none;" id="jdb-add-row-'+name+'"></div>');
			$('#jdb-add-row-'+name).html("");
			$.each(fields, function(index, value) {
				$('#jdb-add-row-'+name).append('<label for="'+index+'">'+value+'</label>');
				$('#jdb-add-row-'+name).append('<input type="text" name="'+index+'" id="'+index+'" />')
			});

			return this;
		},

		/**
	 	 * Opens the dialog to add an entry into the database.
	 	 *
	 	 * @param name	Contains the name of the table that identifies the element, too.
	 	 */
		openAddDialog: function(name) {
			var self = this;
			var element = $('#jdb-add-row-'+name);
			var list = this.getList(name);

			//init buttons
			var buttons = {};
			buttons[list.options.addButtonAddLabel] = function() {
				var data = {};
				$('input[type=text]', element).each(function(index) {
					data[$(this).attr("id")] = $(this).val();
				});
				self.insert(name, data);
				element.dialog("close");
			};
			buttons[list.options.addButtonCancelLabel] = function() {
				element.dialog("close");
			};

			//draw dialog
			element.dialog({
				modal: true,
				buttons: buttons
			});

			return this;
		},

		/**
	 	 * Draws the edit buttons
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param element	Contains the element in that the edit button should be drawn.
	 	 * @param fields	Contains an object with the fields that should be listed, where the key is the real field name and the value is the label for the user. However, fields like "id" or "timestamp" which are autofilled by the system will be spliced.
	 	 * @param options	Contains an object with several options (description see at function "list()").
	 	 */
		drawEditButtons: function(name, element, fields, options) {
			var self = this;

			$('thead tr', element).prepend('<th>&nbsp;</th>');
			$('tbody tr', element).each(function() {
				self.drawEditButton(name, $(this), fields, options);
			});

			//draw edit dialog
			element.append('<div style="display: none;" id="jdb-edit-row-'+name+'"></div>');
			$.each(fields, function(index, value) {
				if ($('#'+index, $('#jdb-edit-row-'+name)).length > 0) return true;
				$('#jdb-edit-row-'+name).append('<label for="'+index+'">'+value+'</label>');
				$('#jdb-edit-row-'+name).append('<input type="text" name="'+index+'" id="'+index+'" />')
			});

			return this;
		},

		/**
		 * Draws an edit button.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param rowElement	Contains the row in that the edit button should be drawn.
	 	 * @param fields	Contains an object with the fields that should be listed, where the key is the real field name and the value is the label for the user. However, fields like "id" or "timestamp" which are autofilled by the system will be spliced.
	 	 * @param options	Contains an object with several options (description see at function "list()").
	 	 */
		drawEditButton: function(name, rowElement, fields, options) {
			var self = this;
			var row = rowElement.data("row");

			//draw button
			rowElement.prepend('<td><a href="#" class="ui-icon ui-icon-pencil">!</a></td>');
			$('a:first', rowElement).click(function() {
				self.openEditDialog(name, rowElement, fields, options);
			});

			return this;
		},

		/**
	 	 * Opens the edit dialog.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param rowElement	Contains the row element in the list.
	 	 * @param fields	Contains an object with the fields that should be listed, where the key is the real field name and the value is the label for the user. However, fields like "id" or "timestamp" which are autofilled by the system will be spliced.
	 	 * @param options	Contains an object with several options (description see at function "list()").
	 	 */
		openEditDialog: function(name, rowElement, fields, options) {
			var self = this;
			var row = rowElement.data("row");
			var element = $('#jdb-edit-row-'+name);

			//set values
			$.each(row, function(index, value) {
				$('#'+index, element).val(value);
			});

			//generate buttons
			buttons = {};
			buttons[options.editButtonEditLabel] = function() {
				var data = {};
				$('input[type=text]', element).each(function() {
					data[$(this).attr("id")] = $(this).val();
					$('.'+$(this).attr("id"), rowElement).fadeOut(options.editValueOutDuraction).html($(this).val()).fadeIn(options.editValueInDuraction);
				});
				self.update(name, row.id, data);

				element.dialog("close");
			};
			buttons[options.editButtonCancelLabel] = function() {
				element.dialog("close");
			};

			//open dialog
			element.dialog({
				resizable: false,
				modal: true,
				buttons: buttons
			});

			return this;
		},

		/**
	 	 * Draws the remove button to each row of the table.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param element	Contains the element in that the add button should be drawn.
	 	 * @param options	Contains an object with several options (description see at function "list()").
	 	 */
		drawRemoveButtons: function(name, element, options) {
			var self = this;

			//draw confirm div
			if (options.removeButtonConfirm) element.append('<div style="display: none;" id="jdb-remove-row-'+name+'">'+options.removeButtonConfirmText+'</div>');
		
			$('thead tr', element).prepend('<th>&nbsp;</th>');
			$('tbody tr', element).each(function() {
				self.drawRemoveButton(name, $(this), options);
			});

			return this;
		},

		/**
	 	 * Draws one remove button.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param rowElement	Contains the element of the row.
	 	 * @param options	Contains several options (description see at function "list()").
	 	 */
		drawRemoveButton: function(name, rowElement, options) {
			var self = this;
			var row = rowElement.data("row");

			rowElement.prepend('<td><a href="#" title="'+options.removeButtonLabel+'" class="ui-icon ui-icon-close">X</a></td>');
			$('a:first', rowElement).click(function() {
				if (!options.removeButtonConfirm) {
					self.remove(name, row.id);
					$(this).parent().parent().fadeOut(1500);
				}else self.openRemoveDialog(name, row.id, $(this).parent().parent(), options);
			});

			return this;
		},

		/**
	 	 * Opens the dialog for removing the row.
	 	 *
	 	 * @param name	Contains the name of the table.
	 	 * @param id	Contains the id from the row that should be removed.
	 	 * @param rowElement	Contains the element of the row that should be removed in the list.
	 	 * @param options	Contains an object with several options.
	 	 */
		openRemoveDialog: function(name, id, rowElement, options) {
			var element = $('#jdb-remove-row-'+name);
			var self = this;

			//init buttons
			var buttons = {};
			buttons[options.removeButtonRemoveLabel] = function() {
				self.remove(name, id);
				rowElement.fadeOut(options.removeOutDuration);
				$(this).dialog("close");
			};
			buttons[options.removeButtonCancelLabel] = function() {
				$(this).dialog("close");
			};

			//draw dialog
			element.dialog({
				resizable: false,
				height: 180,
				modal: true,
				buttons: buttons
			});

			return this;
		},

		/**
	 	 * Draws an admin panel.
	 	 *
	 	 * @param element	Contains the element into that the adminpanel should be drawn.
	 	 */
		adminpanel: function(element) {
			var self = this;
			var tables = this.getTables();
			var tableElement = $('<div id="jdb_admin_tables"></div>');
			var dumpElement = $('`<div id="jdb_admin_dump"></div>');
			var dumpOptions = {drop: true, create: true};

			element.append('<ul><li><a href="#jdb_admin_tables">'+$.jlng('tables')+'</a></li><li><a href="#jdb_admin_dump">'+$.jlng('dump')+'</a></li></ul>')
				.append(tableElement).append(dumpElement);

			//set table lists
			dumpElement.append('<div><div style="width: 300px; float: left;"><select name="jdb_admin_dump_tables" multiple></select>\
				<p><input type="checkbox" name="drop" id="jdb_admin_dump_drop" checked="checked" /><label for="jdb_admin_dump_drop">'+$.jlng("insertDropTable")+'</label></p>\
				<p><input type="checkbox" name="create" id="jdb_admin_dump_create" checked="checked" /><label for="jdb_admin_dump_create">'+$.jlng("insertCreateTable")+'</label></p></div>\
				<textarea cols="80" rows="20" readonly></textarea></div>');
			tableElement.append('<select name="jdb_admin_tables"></select>');
			$('select', tableElement).append('<option value="">'+$.jlng('plsChoose')+'</option>');
			$.each(tables, function(index, table) {
				$('select', tableElement).append('<option value="'+table.name+'">'+table.name+'</option>');
				$('select', dumpElement).append('<option value="'+table.name+'">'+table.name+'</option>');
			});

			$('input[name="drop"]', dumpElement).change(function() {dumpOptions.drop = ($('input[name="drop"]:checked', dumpElement).val() != undefined);dump();});
			$('input[name="create"]', dumpElement).change(function() {dumpOptions.create = ($('input[name="create"]:checked', dumpElement).val() != undefined);dump();});

			//draw table data holder
			tableElement.append('<div id="jdb_admin_table"></div>');

			//set onchange handler
			$('select', tableElement).change(function() {
				if ($(this).val() == '') return;

				$('#jdb_admin_table').html("");

				var fields = {};
				var table = self.getTable($(this).val());
				$.each(table.fields, function(i, v) {
					fields[v] = v;
				});

				self.list($(this).val(), $('#jdb_admin_table'), fields, {});
			});

			//set dump onchange handler
			$('select', dumpElement).change(function() {
				if ($(this).val() == '') return;

				dump();
			});
			
			function dump() {
				var tables = [];
				$.each($('select', dumpElement).val(), function(i, table) {tables.push(self.getTable(table));});
				$('textarea', dumpElement).val(self.dump(tables, dumpOptions));
			}

			//autoselect dump text
			$('textarea', dumpElement).click(function() {this.focus(); this.select();});

			element.tabs();

			return this;
		},

		/**
		 * Exports all the tables in SQL format.
		 *
		 * @param tables	Contains an array with the tables that should be dumped. If undefined, all tables are dumped.
		 * @param options	Contains several options.
		 */
		dump: function(tables, options) {
			var self = this;
			var sql = "";
			if (tables == undefined) tables = this.getTables();
			$.each(tables, function(i, table) {
				sql += self.dumpTable(table, options)+"\n";
			});
			return sql;
		},

		/**
		 * Exports one single table in SQL format.
		 *
		 * @param table	Contains the object of the table.
		 */
		dumpTable: function(table, options) {
			var o = {
				drop: true,
				create: true
			};
			$.extend(o, options);
			var sql = "";
			if (o.create) sql += this.createTableSQL(table, o)+'\n';
			sql += this.insertRowSQL(table, o);
			return sql;
		},

		/**
		 * Returns the SQL syntax for creating that table.
		 *
		 * @param table	Contains the object of the table.
		 * @param options	Contains several options.
		 */
		createTableSQL: function(table, options) {
			var sql = '';
			if (options.drop) sql += 'DROP TABLE IF EXISTS `'+table.name+'`;\n';
			sql += 'CREATE TABLE `'+table.name+'` (';
			var fields = "";

			$.each(table.fields, function(i, field) {
				if (fields.length > 0) fields += ', ';
				switch(field) {
					case 'id':
						fields += '`id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY';
						break;
					case 'timestamp':
						fields += '`timestamp` INT( 13 ) NOT NULL';
						break;
					default:
						fields += '`'+field+'` TEXT NULL';
						break;
				}
			});

			sql += fields+');';

			return sql;
		},

		/**
		 * Returns the sql syntax for inserting all the rows.
		 *
		 * @param table	Contains the table object.
		 * @param options	Contains several options.
		 */
		insertRowSQL: function(table, options) {
			var self = this;
			var sql = 'INSERT INTO `'+table.name+'` (';
			var fields = '';
			var rows = '';

			$.each(table.fields, function(i, field) {
				if (fields.length > 0) fields += ', ';
				fields += '`'+field+'`';
			});

			sql += fields+') VALUES ';

			$.each(this.getRows(table.name), function(i, row) {
				if (row == null) return false;
				if (rows.length > 0) rows += ", ";
				rows += '(';
				rowSQL = '';
				$.each(table.fields, function(i2, field) {
					if (rowSQL.length > 0) rowSQL += ', ';
					if (field == 'id') rowSQL += "'"+(row[field] + 1)+"'";
					else rowSQL += "'"+self.escapeSQL(row[field])+"'";
				});
				rows += rowSQL+')';
			});

			sql += rows+';';
			return sql;
		},

		/**
		 * Escapes the SQL.
		 *
		 * @param sql	Contains the sql.
		 */
		escapeSQL: function(sql) {
			var element = $('<div></div>');
			element.text(sql);
			return element.html();
		}

	});

	var JLNG = $.Class.create({
		code: null,
		variables: {

		},

		/**
	 	 * Initializes the language.
	 	 *
	 	 * @param code	Contains the language code, e.g. "de".
	 	 */
		init: function(code) {
			this.code = code;
			this.variables = {};

			this.initDB().load();
		},

		/**
	 	 * Makes sure that the tables are created.
	 	 */
		initDB: function() {
			$.jdb().createTable(this.getTableName(), ["name", "value"], {
				splitRows: true
			});

			return this;
		},

		/**
	 	 * Loads the variables from the database into the RAM.
	 	 */
		load: function() {
			var self = this;
			var entries = $.jdb().getRows(this.getTableName());

			$.each(entries, function(index, row) {
				if (row == null) return true;

				self.variables[row.name] = row;
			});

			return this;
		},

		/**
	 	 * Returns the name of the database table.
	 	 */
		getTableName: function() {
			return "jlng_variables_"+this.code;
		},

		/**
		 * Sets a language as default and returns it.
		 *
		 * @param code	Contains the language code of the default language.
		 */
		preset: function(code) {
			if ($.jlngInstance[code] == undefined) {
				$.jlngInstance[code] = new JLNG(code);
				$.jlngInstance['current'] = $.jlngInstance[code];
			}else $.jlngInstance['current'] = $.jlngInstance[code];

			return $.jlngInstance['current'];
		},

		/**
		 * Returns the language.
		 *
		 * @param code	Contains the language code of the default language.
		 */
		getLang: function(code) {
			if ($.jlngInstance[code] == undefined) $.jlngInstance[code] = new JLNG(code);

			return $.jlngInstance[code];
		},

		/**
	 	 * Returns the language variable.
	 	 *
	 	 * @param name	Contains the name of the variable.
	 	 */
		get: function(name) {
			if (this.variables[name] == undefined) this.set(name, name);

			return this.variables[name].value;
		},

		/**
	 	 * Sets the name for the language variable.
	 	 *
	 	 * @param name	Cotnains the name of the variable.
		 * @param value	Contains the value of the variable.
	 	 */
		set: function(name, value) {
			if (this.variables[name] == undefined) {
				this.variables[name] = $.jdb().insert(this.getTableName(), {
					name: name,
					value: value
				});
			}else{
				this.variables[name] = $.jdb().update(this.getTableName(), this.variables[name].id, {
					name: name,
					value: value
				});
			}

			return this;
		}

	});

	$.jdbInstance = null;
	$.jdb = function() {
		if ($.jdbInstance == null) $.jdbInstance = new JDB();

		return $.jdbInstance;
	}

	$.jlngInstance = {};

	$.jlng = function(name, value) {
		if ($.jlngInstance['current'] == undefined) {
			$.jlngInstance['en'] = new JLNG('en');
			$.jlngInstance['current'] = $.jlngInstance['en'];
		}
		if (value != undefined) return $.jlngInstance['current'].set(name, value);
		if (name != undefined) return $.jlngInstance['current'].get(name);
		return $.jlngInstance['current'];
	}

})(jQuery);